package com.hippo.core.aspect;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import java.util.Collections;

/**
 * @ClassName TransationAspect
 * @Description TODO 切面实现全局事务,有些框架已经注入了PlatformTransactionManager，不需要再次注入,例如mybatis-plus、spring-data-jpa、spring-boot-starter-jdbc
 * @Author tangxl
 * @create 2023-05-09 09:02
 **/
@Aspect
@Slf4j
@Configuration
public class TransactionAspect {
    /**
     * 多个服务模块中符合以下规范的 public修饰的方法 所有返回值 com.hippo.任意路径.service.impl.任意类.任意方法(任意参数)
     * 注意：只有public修改的方法才会拥有事务功能
     */
    public static final String AOP_POINTCUT_EXPRESSION="execution(public * com.hippo..service.impl.*.*(..))";

    /**
     * 注入事务管理器
     */
    @Resource
    private PlatformTransactionManager transactionManager;


    /**
     * 设置事务管理规则
     * @return
     */
    @SuppressWarnings(value = {"unchecked","rawtypes"}) // 压制警告,不检查,unchecked是指定警告类型,rawtypes是指定警告的级别
    @Bean
    public TransactionInterceptor txAdvice(){
        // 事务管理规则,声明具备事务管理的方法名
        NameMatchTransactionAttributeSource source=new NameMatchTransactionAttributeSource();
        // TODO transactionAttribute：新增、更新、删除
        // 初始化事务管理规则
        RuleBasedTransactionAttribute transactionAttribute=new RuleBasedTransactionAttribute();
        // PROPAGATION_REQUIRED:如果当前存在事务，则加入该事务；如果当前没有事务，则创建一个新的事务。
        transactionAttribute.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 设置抛出异常后回滚事务
        transactionAttribute.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        // 设置事务失效时间,如果超过5秒，则回滚事务
        transactionAttribute.setTimeout(5);
        // TODO supportsAttr：查询
        // 初始化事务管理规则
        RuleBasedTransactionAttribute supportsAttr = new RuleBasedTransactionAttribute();
        // PROPAGATION_SUPPORTS:支持当前事务，如果当前没有事务，就以非事务方式执行
        supportsAttr.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
        // 只读事务，不做更新操作
        supportsAttr.setReadOnly(true);

        // 以当前方法名为key,当前事务管理规则为value,保存在map中
        // addAttr：新增、更新、删除
        source.addTransactionalMethod("add*",transactionAttribute);
        source.addTransactionalMethod("save*",transactionAttribute);
        source.addTransactionalMethod("insert*",transactionAttribute);
        source.addTransactionalMethod("update*",transactionAttribute);
        source.addTransactionalMethod("delete*",transactionAttribute);
        source.addTransactionalMethod("remove*",transactionAttribute);
        source.addTransactionalMethod("batch*",transactionAttribute);
        source.addTransactionalMethod("create*",transactionAttribute);
        source.addTransactionalMethod("modify*",transactionAttribute);
        source.addTransactionalMethod("edit*",transactionAttribute);
        source.addTransactionalMethod("do*",transactionAttribute);
        source.addTransactionalMethod("export*",transactionAttribute);
        // supportsAttr：查询
        source.addTransactionalMethod("get*",supportsAttr);
        source.addTransactionalMethod("query*",supportsAttr);
        source.addTransactionalMethod("find*",supportsAttr);
        source.addTransactionalMethod("select*",supportsAttr);
        return new TransactionInterceptor(transactionManager,source);
    }

    /**
     * Aop配置定义切面和切点的信息
     * @return
     */
    @Bean
    public Advisor txAdviceAdvisor(){
        // 声明切点的面
        AspectJExpressionPointcut pointcut=new AspectJExpressionPointcut();
        // 设置切面=切点pointcut+通知TxAdvice
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        return new DefaultPointcutAdvisor(pointcut,txAdvice());
    }
}
